package com.resifind.www.util;

import com.resifind.www.exception.BaseException;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * @author 36069
 */
public class EncodeUtil {
    /**
     * 盐的长度
     */
    private static final int SALT_LENGTH = 16;
    /**
     * hash算法
     */
    private static final String HASH_ALGORITHM = "SHA-256";

    /**
     * 生成随机盐
     *
     * @return 随机盐
     */
    public static byte[] generateSalt() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] salt = new byte[SALT_LENGTH];
        secureRandom.nextBytes(salt);
        return salt;
    }

    /**
     * 编码字符串(Base64)
     *
     * @param str  字符串
     * @param salt 盐
     * @return 含盐的hash字符串
     */
    public static String encode(String str, byte[] salt) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(HASH_ALGORITHM);
            //盐与字符串合并(防彩虹表攻击)
            messageDigest.update(salt);
            byte[] hash = messageDigest.digest(str.getBytes());
            //转换为Base64编码
            return Base64.getEncoder().encodeToString(hash);
        } catch (NoSuchAlgorithmException e) {
            throw new BaseException(500, "编码失败");
        }
    }

    /**
     * 验证字符串是否匹配
     *
     * @param str  字符串
     * @param salt 盐字符串(Base64)
     * @param hash hash字符串
     * @return 是否匹配
     */
    public static boolean verify(String str, String salt, String hash) {
        if (salt == null || hash == null) {
            return false;
        }
        //将Base64编码的盐转换为字节数组
        byte[] saltBytes = Base64.getDecoder().decode(salt);
        //将Base64编码的hash转换为字节数组
        byte[] expectedHash = Base64.getDecoder().decode(hash);
        try {
            //使用相同的hash算法和盐计算hash值
            MessageDigest messageDigest = MessageDigest.getInstance(HASH_ALGORITHM);
            messageDigest.update(saltBytes);
            byte[] computedHash = messageDigest.digest(str.getBytes());
            //比较hash，防时间攻击
            return MessageDigest.isEqual(computedHash, expectedHash);
        } catch (NoSuchAlgorithmException e) {
            throw new BaseException(500, "编码验证失败");
        }
    }
}
